import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Observable;
/**
* Abstract logical representation of a JComponent that
* should be displayed in a JLayeredPane.
* @author Florian Minges
*/
public abstract class AbstractComponentModel extends Observable
implements PropertyChangeListener { //doesn't need the propertychangelistener, but the propertyChange-method
private int componentWidth;
private int componentHeight;
private Point position;
private boolean isVisible;
public void initialize() {
setPosition(0, 0);
}
/** Moves the position by the given length. */
public void move(int dx, int dy) {
int x = getXPosition() + dx;
int y = getYPosition() + dy;
setPosition(x, y);
}
/** Returns the position of this label, ie the position of
* the upper left corner. */
public Point getPosition() {
return new Point(getXPosition(), getYPosition());
}
/** Returns the position of this label, ie the position to
* where it points. */
public Point getPointerPosition() {
return new Point(getXPointerPosition(), getYPointerPosition());
}
/** Returns the x-position of the components
* top left corner. */
public int getXPosition() {
return this.position.x;
}
/** Returns the y-position of the components
* top left corner. */
public int getYPosition() {
return this.position.y;
}
/** Returns the x-position of where the components
* graphical representation points. */
public int getXPointerPosition() {
return getXPosition() + getXOffset();
}
/** Returns the y-position of where the components
* graphical representation points. */
public int getYPointerPosition() {
return getYPosition() + getYOffset();
}
/** Returns the distance from the stored position to
* the X and Y position that it actually points at. */
protected abstract int getXOffset();
protected abstract int getYOffset();
/** Sets the position. */
public void setPosition(Point p) {
setPosition(p.x, p.y);
}
/** Sets the position. */
public void setPosition(int x, int y) {
this.position = new Point(x, y);
setChanged();
notifyObservers(Global.POSITION_UPDATE);
}
/** Sets the stored position so that it's graphical
* representation points towards the given position. */
public void setPointerPosition(Point p) {
setPointerPosition(p.x, p.y);
}
/** Sets the stored position so that it's graphical
* representation points towards the given position. */
public void setPointerPosition(int x, int y) {
x -= getXOffset();
y -= getYOffset();
setPosition(x, y);
}
/** Sets the new position in case of a zoom. */
protected abstract void handleZoom();
/** If the map moves, the overlayLabels move with it!
* The parameters suppose the use of a firePropertyChange. */
private void handleDrag(Object oldValue, Object newValue) {
Point oldPoint = (Point) oldValue;
Point newPoint = (Point) newValue;
int dx = oldPoint.x - newPoint.x;
int dy = oldPoint.y - newPoint.y;
move(dx, dy);
}
/** Responds to map-actions like drag and zoom. */
@Override
public void propertyChange(PropertyChangeEvent event) {
String property = event.getPropertyName();
if (property.equals(Global.DRAG_EVENT) && isVisible()) { //don't have to move invisible markers
handleDrag(event.getOldValue(), event.getNewValue());
} else if (property.startsWith(Global.ZOOM)){
handleZoom();
}
}
public int getComponentWidth() {
return this.componentWidth;
}
public void setComponentWidth(int componentWidth) {
if (componentWidth > 0) {
this.componentWidth = componentWidth;
waitForSecondChangeBeforeNotification();
}
}
public int getComponentHeight() {
return this.componentHeight;
}
public void setComponentHeight(int componentHeight) {
if (componentHeight > 0) {
this.componentHeight = componentHeight;
waitForSecondChangeBeforeNotification();
}
}
/** As one don't want to notify Observer two times when the component
* size is updated (width and height), this method only notifies observers
* if property 'hasChanged' already was set to true. */
private void waitForSecondChangeBeforeNotification() {
if (hasChanged())
notifyObservers(Global.COMPONENT_SIZE_UPDATE);
else
setChanged();
}
public void setProperComponentSize() {
setComponentWidth(getProperComponentWidth());
setComponentHeight(getProperComponentHeight());
}
public abstract int getProperComponentWidth();
public abstract int getProperComponentHeight();
public void setVisible(boolean visible) {
this.isVisible = visible;
setChanged();
notifyObservers(Global.VISIBILITY_UPDATE);
}
public boolean isVisible() {
return this.isVisible;
}
public boolean intersects(AbstractComponentModel model) {
Rectangle thisRectangle = this.getRectangle();
Rectangle otherRectangle = model.getRectangle();
return thisRectangle.intersects(otherRectangle) || otherRectangle.intersects(thisRectangle);
}
public Rectangle getRectangle() {
return new Rectangle(getXPosition(), getYPosition(),
getComponentWidth(), getComponentHeight());
}
public Dimension getSize() {
return new Dimension(getComponentWidth(), getComponentHeight());
}
public Integer getLayer() {
return new Integer(position.y + getComponentHeight());
}
}